﻿#include <libini/document.h>
#include <libini/string.h>


// constructor
Xanadu::ini::document::document(const std::string& _FilePath) noexcept
{
	this->_memberSectionArray.clear();
	this->from_file(_FilePath);
}

// constructor
Xanadu::ini::document::document(const std::wstring& _FilePath) noexcept
{
	this->_memberSectionArray.clear();
	this->from_file(_FilePath);
}

// constructor
Xanadu::ini::document::document(const std::vector<char>& _Bytes) noexcept
{
	this->_memberSectionArray.clear();
	this->from_bytes(_Bytes);
}

// constructor
Xanadu::ini::document::document(const char* _Bytes, std::size_t _Length) noexcept
{
	this->_memberSectionArray.clear();
	this->from_memory(_Bytes, _Length);
}

// destructor
Xanadu::ini::document::~document() noexcept
{
	this->_memberSectionArray.clear();
}



// Analysis document, returns all section
std::list<std::list<std::string>> Xanadu::ini::document::split_section(const std::list<std::string>& _Strings) noexcept
{
	auto 		vSectionArray = std::list<std::list<std::string>>();

	// Reverse lookup section
	auto 		vFindEnd = _Strings.rbegin();
	for(auto vIterator = _Strings.rbegin(); vIterator != _Strings.rend(); ++vIterator)
	{
		if(Xanadu::ini::string::is_section(*vIterator))
		{
			auto 		vSectionData = std::list<std::string>(vIterator.base(), vFindEnd.base());
			vSectionData.insert(vSectionData.begin(), *vIterator);
			++vIterator;
			// Find all comments up
			while(vIterator != _Strings.rend() && (Xanadu::ini::string::is_empty(*vIterator) || Xanadu::ini::string::is_explain(*vIterator)))
			{
				vSectionData.insert(vSectionData.begin(), *vIterator);
				++vIterator;
			}
			vSectionArray.insert(vSectionArray.begin(), vSectionData);
			vFindEnd = vIterator;
			if(vIterator == _Strings.rend())
			{
				break;
			}
		}
	}
	return vSectionArray;
}



// Build from file.
bool Xanadu::ini::document::from_file(const std::string& _FilePath) noexcept
{
	auto 		vError = false;
	std::ifstream	vIFStream;
	vIFStream.open(_FilePath, std::ios::in | std::ios::binary);
	if(vIFStream.is_open())
	{
		// Get file size
		vIFStream.seekg(0, std::ios_base::end);
		auto			vFileSize = vIFStream.tellg();
		vIFStream.seekg(0, std::ios_base::beg);

		// Loop read until the end of the file
		std::vector<char>	vBytes(vFileSize);
		long long		vPos = 0;
		char 			vTempBuffer[1024] = {0};
		std::size_t 		vTempLength = 1024;
		while (!vIFStream.eof())
		{
			vIFStream.read(vTempBuffer, vTempLength);
			auto		vCount = vIFStream.gcount();
			if(vCount > 0)
			{
				std::memcpy(vBytes.data() + vPos, vTempBuffer, vCount);
				vPos += vCount;
			}
		}
		if(vIFStream.eof())
		{
			vError = this->from_bytes(vBytes);
		}
		vIFStream.close();
	}
	return vError;
}

// Build from file.
bool Xanadu::ini::document::from_file(const std::wstring& _FilePath) noexcept
{
	auto 		vError = false;
	std::ifstream		vIFStream;
	vIFStream.open(_FilePath, std::ios::in | std::ios::binary);
	if(vIFStream.is_open())
	{
		// Get file size
		vIFStream.seekg(0, std::ios_base::end);
		auto			vFileSize = vIFStream.tellg();
		vIFStream.seekg(0, std::ios_base::beg);

		// Loop read until the end of the file
		std::vector<char>	vBytes(vFileSize);
		long long		vPos = 0;
		char 			vTempBuffer[1024] = {0};
		std::size_t 		vTempLength = 1024;
		while (!vIFStream.eof())
		{
			vIFStream.read(vTempBuffer, vTempLength);
			auto		vCount = vIFStream.gcount();
			if(vCount > 0)
			{
				std::memcpy(vBytes.data() + vPos, vTempBuffer, vCount);
				vPos += vCount;
			}
		}
		if(vIFStream.eof())
		{
			vError = this->from_bytes(vBytes);
		}
		vIFStream.close();
	}
	return vError;
}

// Build from byte array
bool Xanadu::ini::document::from_bytes(const std::vector<char>& _Bytes) noexcept
{
	return this->from_string(std::string(_Bytes.data(), _Bytes.size()));
}

// Build from string
bool Xanadu::ini::document::from_string(const std::string& _String) noexcept
{
	auto 		vStrings = Xanadu::ini::string::split(_String, LIBINI_NEWLINE_STRING);

	// Check all rows here to make sure they are standard data
	for(const auto & vLine : vStrings)
	{
		if(Xanadu::ini::string::is_error(vLine))
		{
			return false;
		}
	}

	// Split all section
	auto 		vSectionItems = Xanadu::ini::document::split_section(vStrings);
	for(const auto & vSectionData : vSectionItems)
	{
		auto 		vSectionObject = Xanadu::ini::section::fromStrings(vSectionData);
		this->_memberSectionArray.insert(std::map<std::string, Xanadu::ini::section>::value_type(vSectionObject.name(), vSectionObject));
	}

	return true;
}

// Build from memory
bool Xanadu::ini::document::from_memory(const char* _Bytes, std::size_t _Length) noexcept
{
	if(_Bytes == nullptr && _Length == 0)
	{
		return false;
	}
	return Xanadu::ini::document::from_string(std::string(_Bytes, _Length));
}



// Serialize to file
bool Xanadu::ini::document::to_file(const std::string& _FilePath) noexcept
{
	auto 		vError = false;
	auto 		vString = Xanadu::ini::document::to_string();
	if(vString.empty())
	{
		return vError;
	}
	std::ofstream 	vOFStream(_FilePath, std::ios::out | std::ios::binary);
	if(vOFStream.is_open())
	{
		vOFStream.write(vString.data(), vString.size());
		vOFStream.close();
		vError = true;
	}
	return vError;
}

// Serialize to file
bool Xanadu::ini::document::to_file(const std::wstring& _FilePath) noexcept
{
	auto 		vError = false;
	auto 		vString = Xanadu::ini::document::to_string();
	if(vString.empty())
	{
		return vError;
	}
	std::ofstream 	vOFStream(_FilePath, std::ios::out | std::ios::binary);
	if(vOFStream.is_open())
	{
		vOFStream.write(vString.data(), vString.size());
		vOFStream.close();
		vError = true;
	}
	return vError;
}

// Serialize to byte array
std::vector<char> Xanadu::ini::document::to_bytes() noexcept
{
	auto 		vString = Xanadu::ini::document::to_string();
	auto 		vVector = std::vector<char>(vString.size());
	std::memcpy(vVector.data(), vString.data(), vString.size());
	return vVector;
}

// Serialize to string
std::string Xanadu::ini::document::to_string() noexcept
{
	auto 		vString = std::string();
	for(const auto & vSection : _memberSectionArray)
	{
		vString += vSection.second.toString();
		vString += LIBINI_NEWLINE_STRING;
	}
	return vString;
}



// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, bool _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, _Value ? std::string("1") : std::string("0"));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, char _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, unsigned char _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, short _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, unsigned short _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, int _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, unsigned int _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, long _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, unsigned long _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, long long _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, unsigned long long _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, float _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, double _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, long double _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::to_string(_Value));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, const std::vector<char>& _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::string(_Value.data(), _Value.size()));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, const std::vector<unsigned char>& _Value) noexcept
{
	return Xanadu::ini::document::set_value(_Section, _Key, std::string((const char*)(_Value.data()), _Value.size()));
}

// Set value
bool Xanadu::ini::document::set_value(const std::string& _Section, const std::string& _Key, const std::string& _Value) noexcept
{
	auto 		vIterator = this->_memberSectionArray.find(_Section);
	if(vIterator == this->_memberSectionArray.end())
	{
		this->_memberSectionArray.insert(std::map<std::string, Xanadu::ini::section>::value_type(_Section, Xanadu::ini::section()));
		vIterator = this->_memberSectionArray.find(_Section);
		if(vIterator == this->_memberSectionArray.end())
		{
			return false;
		}
	}
	vIterator->second.set(_Key, _Value);
	return true;
}



// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, bool& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		if(vString.size() == 1 && vString[0] == '0')
		{
			_Value = false;
		}
		else if(vString.size() == 5 && Xanadu::ini::string::to_lower(vString) == "false")
		{
			_Value = false;
		}
		else
		{
			_Value = true;
		}
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, char& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = (char)std::strtol(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, unsigned char&& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = (unsigned char)std::strtoul(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, short& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = (short)std::strtol(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, unsigned short& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = (unsigned short)std::strtoul(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, int& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = (int)std::strtol(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, unsigned int& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = (unsigned int)std::strtoul(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, long& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = std::strtol(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, unsigned long& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = std::strtoul(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, long long& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = std::strtoll(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, unsigned long long& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = std::strtoull(vString.data(), nullptr, 10);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, float& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = std::strtof(vString.data(), nullptr);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, double& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = std::strtod(vString.data(), nullptr);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, long double& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value = std::strtold(vString.data(), nullptr);
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, std::vector<char>& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value.resize(vString.size());
		std::memcpy(_Value.data(), vString.data(), vString.size());
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, std::vector<unsigned char>& _Value) noexcept
{
	auto 		vString = std::string();
	auto 		vSync = Xanadu::ini::document::value(_Section, _Key, vString);
	if(!vString.empty())
	{
		_Value.resize(vString.size());
		std::memcpy(_Value.data(), vString.data(), vString.size());
	}
	return vSync;
}

// Get value
bool Xanadu::ini::document::value(const std::string& _Section, const std::string& _Key, std::string& _Value) noexcept
{
	auto 		vIterator = this->_memberSectionArray.find(_Section);
	if(vIterator == this->_memberSectionArray.end())
	{
		return false;
	}
	auto 		vItem = Xanadu::ini::item();
	if(!vIterator->second.find(_Key, vItem))
	{
		return false;
	}
	_Value = vItem.value();
	return true;
}
